<template>
<article class="page page-sudoku-decrypt container">
	<h4 class="text-center">数独求解</h4>
	<form class="content">
		<fieldset>
			<legend>数独求解</legend>
			<div class="alert alert-info" role="alert">
				<div>1、简化/单步调试，根据已有的数据推测出所有空格的所有可能性。</div>
				<div>2、暴力破解，在已有的数据基础上尝试各种可能性直至得出正确答案。</div>
				<div>3、保存进度，可以暂存当前状态，点重置时会优先回退到此，若无保存状态点重置时会清空表格。</div>
			</div>
			<div class="table-caption row-flex" align="center">
				<div class="row-flex-main text-left">剩余空格{{blankCount}}个，剩余枚举次数{{possibilityCount}}次</div>
				<div class="row-flex-suffix">
					<button type="button" class="btn btn-link" @click="lockChange" :title="formDataLock ? '解锁' : '锁定'">
						<i :class="['fa', formDataLock ? 'fa-lock' : 'fa-unlock']"></i>
					</button>
				</div>
			</div>
			<table class="table table-bordered">
				<tbody>
					<tr v-for="(v,i) in 9" :key="`tr_${i}`">
						<td v-for="(item, j) in 9" :key="`td_${i}_${j}`">
							<input type="text" class="form-control" v-model.trim="formData[`${i}_${j}`]" :readonly="!!(formDataLock || {})[`${i}_${j}`]">
							<div v-if="cellNums[`${i}_${j}`]" class="cell-mask cell-mask-success">{{cellNums[`${i}_${j}`].join(',')}}</div>
							<div v-else-if="showMask && !formData[`${i}_${j}`] && !cellNums[`${i}_${j}`]" class="cell-mask cell-mask-danger">ERROR</div>
						</td>
					</tr>
				</tbody>
			</table>
			<div class="row">
				<div class="col-3">
					<x-button block @click="resetData" :loading="loadingCount > 0">重置</x-button>
				</div>
				<div class="col-3">
					<x-button block type="primary" @click="saveData" :loading="loadingCount > 0">保存进度（{{formDataBak ? '有' : '无'}}）</x-button>
				</div>
				<div class="col-3">
					<x-button block type="primary" @click="getCellsNums" :loading="loadingCount > 0">简化/单步调试</x-button>
				</div>
				<div class="col-3">
					<x-button block type="primary" @click="submit" :loading="loadingCount > 0">暴力破解</x-button>
				</div>
			</div>
		</fieldset>
	</form>
</article>
</template>

<script>
let {deepCopy}  = global.$jstools || {}
let formData = {}
for (let i = 0; i < 9; i++) {
	for (let j = 0; j < 9; j++) {
		formData[`${i}_${j}`] = ''
	}
}

export default {
	name: 'sudoku-decrypt',
	data () {
		return {
			formData: deepCopy(formData),
			formDataBak: null,
			formDataLock: null,
			cellNums: {},
			showMask: false,
			allPossibility: [],
			loadingCount: 0
		}
	},
	computed: {
		blankCount () {
			let result = Object.keys(this.cellNums).length
			return result
		},
		possibilityCount () {
			let result = 0
			let values = Object.values(this.cellNums)
			if (values.length) {
				result = 1
				values.forEach(v => {
					result *= v.length
				})
			}
			return result
		}
	},
	methods: {
		// 变更锁定状态
		lockChange () {
			if (this.formDataLock) {
				this.formDataLock = null
			} else {
				this.formDataLock = deepCopy(this.formData)
			}
		},
		// 重置
		resetData () {
			if (this.formDataBak) {
				this.formData = this.formDataBak
				this.formDataBak = null
			} else if (this.formDataLock) {
				this.formData = deepCopy(this.formDataLock)
			} else {
				this.formData = deepCopy(formData)
			}
			this.cellNums = {}
			this.allPossibility = []
			this.showMask = false
		},
		// 保存进度
		saveData () {
			this.formDataBak = deepCopy(this.formData)
			this.$toast('已保存当前状态')
		},
		// 计算
		submit () {
			this.getCellsNums(true)
			this.loadingCount++
			this.$nextTick(() => {
				let result = {flag: false, formData: {}}
				if (this.blankCount > 0) {
					let key = Object.keys(this.cellNums)[0]
					let [x, y] = key.split('_')
					let values = this.cellNums[key]
					for (let i = 0; i < values.length; i++) {
						let r = this.assumedValue(x, y, values[i])
						if (r.flag) {
							result = r
							break
						}
					}
				}
				if (result.flag) {
					this.formData = result.formData
					this.cellNums = {}
					this.allPossibility = []
				} else {
					this.$toast('未找到正确答案')
				}
				this.loadingCount--
			})
		},
		// 获取所有格子的所有可能性
		getCellsNums (flag) {
			this.loadingCount++
			this.showMask = true
			let possibilityCount = this.possibilityCount
			let blankCount = this.blankCount
			this.cellNums = {}
			for (let i = 0; i < 9; i++) {
				for (let j = 0; j < 9; j++) {
					let num = parseInt(this.formData[`${i}_${j}`])
					if (!num || !(num >0 && num < 10)) {
						this.getCellItemNums(i, j)
					}
				}
			}
			if (flag) {
				this.getCellsNums()
			} else if (this.possibilityCount === possibilityCount) {
				this.$toast('已经不能再简化了')
			} else if (blankCount !== this.blankCount) {
				this.getCellsNums()
			}
			this.loadingCount--
		},
		// 获取某个格子的所有可能性
		getCellItemNums (x, y) {
			let result = this.getBlankCellItemNums(x, y, this.formData)
			let resultLen = result.length
			if (resultLen === 1) {
				this.$set(this.formData, `${x}_${y}`, result[0])
			} else if (resultLen > 1) {
				for (let i = resultLen - 1; i >= 0; i--) {
					let flag = (this.assumedValueCheck(x, y, result[i]) || {}).flag
					if (!flag) {result.splice(i, 1)}
				}
				if (result.length === 1) {
					this.$set(this.formData, `${x}_${y}`, result[0])
				} else if (result.length > 1) {
					this.cellNums[`${x}_${y}`] = result
				}
			}
		},
		// 核查可能的所有结果(性能差，废弃)
		// checkResults () {
		// 	if (this.possibilityCount > 100000000) {
		// 		this.$toast('工作量太大，罢工了')
		// 		return
		// 	}
		// 	let _this = this
		// 	this.showMask = true
		// 	let hasResult = false
		// 	let keys = Object.keys(this.cellNums)
		// 	let depth = 0
		// 	doExchange({formData: this.formData, keys, depth})
		// 	// 递归
		// 	function doExchange ({formData, keys, depth}) {
		// 		if (hasResult) return
		// 		formData = deepCopy(formData)
		// 		let values = _this.cellNums[keys[depth]]
		// 		for (let i = 0; i < values.length; i++) {
		// 			formData[keys[depth]] = values[i]
		// 			if (depth != keys.length - 1) {
		// 				doExchange({formData, keys, depth: depth + 1})
		// 			} else {
		// 				if (_this.checkResultItem(formData)) {
		// 					_this.formData = formData
		// 					hasResult = true
		// 					return
		// 				}
		// 			}
		// 		}
		// 	}
		// },
		// 遍历可能得到的所有结果(性能差，废弃)
		// _getResults () {
		// 	this.results = [deepCopy(this.formData)]
		// 	let _keys = Object.keys(this.cellNums)
		// 	let _index = 0
		// 	while (_keys[_index]) {
		// 		let key = _keys[_index]
		// 		let results = []
		// 		this.cellNums[key].forEach(v => {
		// 			this.results.forEach(item => {
		// 				let _item = deepCopy(item)
		// 				_item[key] = v
		// 				results.push(_item)
		// 			})
		// 		})
		// 		this.results = results
		// 		_index++
		// 	}
		// },
		// 核查得到的所有结果
		// _checkResults () {
		// 	let len = this.results.length
		// 	for (let index = 0; index < len; index++) {
		// 		let result = this.results[index]
		// 		if (this.checkResultItem(result)) {
		// 			this.formData = result
		// 			return
		// 		}
		// 	}
		// },
		// 核查得到的某个结果
		checkResultItem (item) {
			// 行
			for (let i = 0; i < 9; i++) {
				let row = []
				for (let j = 0; j < 9; j++) {
					let num = parseInt(item[`${i}_${j}`])
					if (row.includes(num)) {
						return false
					} else {
						row.push(num)
					}
				}
			}
			// 列
			for (let i = 0; i < 9; i++) {
				let col = []
				for (let j = 0; j < 9; j++) {
					let num = parseInt(item[`${j}_${i}`])
					if (col.includes(num)) {
						return false
					} else {
						col.push(num)
					}
				}
			}
			// 九宫格
			for (let i = 0; i < 9; i+=3) {
				for (let j = 0; j < 9; j+=3) {
					let flag = this.checkGridItem({
						formData: item,
						rol: i,
						col: j
					})
					if (!flag) {
						return false
					}
				}
			}
			return true
		},
		// 核查单个九宫格
		checkGridItem ({formData, rol, col}) {
			let grid = []
			let startX = Math.floor(rol / 3) * 3
			let startY = Math.floor(col / 3) * 3
			let endX = startX + 3
			let endY = startY + 3
			for (let i = startX; i < endX; i++) {
				for (let j = startY; j < endY; j++) {
					let num = parseInt(formData[`${i}_${j}`])
					if (grid.includes(num)) {
						return false
					} else {
						grid.push(num)
					}
				}
			}
			return true
		},
		// 2022.05.26算法优化：代入假设值递归推进
		assumedValue (x, y, value, formData, allPossibility) {
			let result = {flag: false, formData: {}}
			formData = deepCopy(formData || this.formData)
			if (!allPossibility) {
				allPossibility = []
				for (let key in this.cellNums) {
					if (key === `${x}_${y}`) continue
					allPossibility.push({key, values: this.cellNums[key]})
				}
			}
			let checkResult = this.assumedValueCheck(x, y, value, formData) || {}
			if (checkResult.flag) {
				formData[`${x}_${y}`] = value
				if (checkResult.allPossibility && checkResult.allPossibility.length) {
					let nextObj = checkResult.allPossibility.shift()
					let nextValues = nextObj.values
					let [nextX, nextY] = nextObj.key.split('_')
					for (let i = 0; i < nextValues.length; i++) {
						let _result = this.assumedValue(nextX, nextY, nextValues[i], formData, nextObj.allPossibility)
						if (_result.flag) {
							result.flag = true
							result.formData = _result.formData
							break
						} else {
							_result.flag = false
						}
					}
				} else {
					let blanks = Object.values(formData).filter(v => !v)
					result.flag = !(blanks.length)
					result.formData = formData
				}
			} else {
				result.flag = false
				result.formData = formData
			}
			return result
		},
		// 2022.05.24算法优化：代入假设值进行计算，看此路是否能通
		assumedValueCheck (x, y, value, formData) {
			let result = {flag: true, allPossibility: []}
			if (!formData) {
				formData = deepCopy(this.formData)
			}
			formData[`${x}_${y}`] = value
			for (let i = 0; i < 9; i++) {
				for (let j = 0; j < 9; j++) {
					let num = parseInt(formData[`${i}_${j}`])
					if (!num || !(num >0 && num < 10)) {
						let nums = this.getBlankCellItemNums(i, j, formData)
						if (nums.length === 0) {
							result.flag = false
							return result
						} else {
							result.allPossibility.push({
								key: `${i}_${j}`, values: nums
							})
						}
					}
				}
			}
			return result
		},
		// 2022.05.24算法优化：获取某个格子的所有可能性
		getBlankCellItemNums (x, y, formData) {
			let result = [1, 2, 3, 4, 5, 6, 7, 8, 9]
			// 九宫格
			let startX = Math.floor(x / 3) * 3
			let startY = Math.floor(y / 3) * 3
			let endX = startX + 3
			let endY = startY + 3
			for (let i = startX; i < endX; i++) {
				for (let j = startY; j < endY; j++) {
					let num = parseInt(formData[`${i}_${j}`])
					let index = result.indexOf(num)
					if (index > -1) {
						result.splice(index, 1)
					}
				}
			}
			// 行&列
			for (let i = 0; i < 9; i++) {
				let num1 = parseInt(formData[`${x}_${i}`])
				let index1 = result.indexOf(num1)
				if (index1 > -1) {
					result.splice(index1, 1)
				}
				let num2 = parseInt(formData[`${i}_${y}`])
				let index2 = result.indexOf(num2)
				if (index2 > -1) {
					result.splice(index2, 1)
				}
			}
			return result
		},
	}
}
</script>

<style scoped lang="less">
@import "~@/assets/css/_mixins.less";
@import "~@/assets/css/_color.less";
.page-sudoku-decrypt {
	display: flex;
	flex-direction: column;
	.content {
		height: 0;
		flex-grow: 1;
		overflow: hidden auto;
		.alert {
			line-height: 2;
			margin-bottom: .5rem;
		}
		.table-caption {
			font-family: 黑体;
			line-height: 2;
		}
		.table {
			text-align: center;
			border-left: 2px solid #999;
			border-top: 2px solid #999;
			tr:nth-child(3n) {
				border-bottom-width: 2px;
				border-bottom-color: #999;
			}
			td:nth-child(3n) {
				border-right-width: 2px;
				border-right-color: #999;
			}
			td {
				position: relative;
				.cell-mask {
					.absolute-fullscreen;
					opacity: .3;
					pointer-events: none;
					.flex-center;
					&.cell-mask-success {
						color: #045204;
						background-color: rgba(0, 255, 0, .2);
					}
					&.cell-mask-danger {
						color: #6f1010;
						background-color: rgba(255, 0, 0, .2);
					}
				}
			}
		}
	}
}
</style>
